Хотим провести кластеризацию футбольных игроков из датасета футбольного симулятора FIFA 22.
Футболисты играют на разных позициях, однако такие обобщения позиций, как атака, полузащита, и защита, весьма неточны и по тем же причинам не указываются в данных. В то же время, такие обобщения весьма полезны для простого описания для игрока.
Наше предположение состоит в том, что на основании рейтингов игроков по различным характеристикам кластеризация поможет достаточно неплохо различать игроков атаки, полузащиты, и защиты.
Начнём с библиотек.
#install.packages(c("knitr", "kableExtra", "magick",
# "ggplot2", "reshape2", "viridis",
# "stringr", "FactoMineR", "factorextra",
# "kohonen", "mclust", "dbscan", "easyr"))
library(knitr)
library(kableExtra) #красивые html таблицы
library(magick) #работа с картинками## Linking to ImageMagick 6.9.9.39
## Enabled features: cairo, fontconfig, freetype, lcms, pango, rsvg, webp
## Disabled features: fftw, ghostscript, x11
## Loading required package: viridisLite
library(stringr) #работа с текстом
library(FactoMineR) #факторный анализ
library(factoextra) #факторный анализ## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
## Package 'mclust' version 5.4.5
## Type 'citation("mclust")' for citing this R package in publications.
##
## Attaching package: 'mclust'
## The following object is masked from 'package:kohonen':
##
## map
Читаем датасет из csv файла и смотрим на размерность.
## [1] 19239 107
На примере двух известных футболистов покажем, какие переменные у нас имеются. Вот так, например, выглядит строчка для Лионеля Месси:
| 1 | 3 | ||
|---|---|---|---|
| sofifa_id | 1 | 158023 | 20801 |
| player_url | 2 | https://sofifa.com/player/158023/lionel-messi/220002 | https://sofifa.com/player/20801/c-ronaldo-dos-santos-aveiro/220002 |
| short_name | 3 | L. Messi | Cristiano Ronaldo |
| long_name | 4 | Lionel Andrés Messi Cuccittini | Cristiano Ronaldo dos Santos Aveiro |
| player_positions | 5 | RW, ST, CF | ST, LW |
| overall | 6 | 93 | 91 |
| potential | 7 | 93 | 91 |
| value_eur | 8 | 78000000 | 45000000 |
| wage_eur | 9 | 320000 | 270000 |
| age | 10 | 34 | 36 |
| dob | 11 | 1987-06-24 | 1985-02-05 |
| height_cm | 12 | 170 | 187 |
| weight_kg | 13 | 72 | 83 |
| club_name | 14 | Paris Saint-Germain | Manchester United |
| league_name | 15 | French Ligue 1 | English Premier League |
| league_level | 16 | 1 | 1 |
| club_position | 17 | RW | ST |
| club_jersey_number | 18 | 30 | 7 |
| club_loaned_from | 19 | ||
| club_joined | 20 | 2021-08-10 | 2021-08-27 |
| club_contract_valid_until | 21 | 2023 | 2023 |
| nationality | 22 | Argentina | Portugal |
| nation_position | 23 | RW | ST |
| nation_jersey_number | 24 | 10 | 7 |
| preferred_foot | 25 | Left | Right |
| weak_foot | 26 | 4 | 4 |
| skill_moves | 27 | 4 | 5 |
| international_reputation | 28 | 5 | 5 |
| work_rate | 29 | Medium/Low | High/Low |
| body_type | 30 | Unique | Unique |
| real_face | 31 | Yes | Yes |
| release_clause_eur | 32 | 144300000 | 83300000 |
| player_tags | 33 | #Dribbler, #Distance Shooter, #FK Specialist, #Acrobat, #Clinical Finisher, #Complete Forward | #Aerial Threat, #Dribbler, #Distance Shooter, #Crosser, #Acrobat, #Clinical Finisher, #Complete Forward |
| player_traits | 34 | Finesse Shot, Long Shot Taker (AI), Playmaker (AI), Outside Foot Shot, One Club Player, Chip Shot (AI), Technical Dribbler (AI) | Power Free-Kick, Flair, Long Shot Taker (AI), Speed Dribbler (AI), Outside Foot Shot |
| pace | 35 | 85 | 87 |
| shooting | 36 | 92 | 94 |
| passing | 37 | 91 | 80 |
| dribbling | 38 | 95 | 87 |
| defending | 39 | 34 | 34 |
| physic | 40 | 65 | 75 |
| attacking_crossing | 41 | 85 | 87 |
| attacking_finishing | 42 | 95 | 95 |
| attacking_heading_accuracy | 43 | 70 | 90 |
| attacking_short_passing | 44 | 91 | 80 |
| attacking_volleys | 45 | 88 | 86 |
| skill_dribbling | 46 | 96 | 88 |
| skill_curve | 47 | 93 | 81 |
| skill_fk_accuracy | 48 | 94 | 84 |
| skill_long_passing | 49 | 91 | 77 |
| skill_ball_control | 50 | 96 | 88 |
| movement_acceleration | 51 | 91 | 85 |
| movement_sprint_speed | 52 | 80 | 88 |
| movement_agility | 53 | 91 | 86 |
| movement_reactions | 54 | 94 | 94 |
| movement_balance | 55 | 95 | 74 |
| power_shot_power | 56 | 86 | 94 |
| power_jumping | 57 | 68 | 95 |
| power_stamina | 58 | 72 | 77 |
| power_strength | 59 | 69 | 77 |
| power_long_shots | 60 | 94 | 93 |
| mentality_aggression | 61 | 44 | 63 |
| mentality_interceptions | 62 | 40 | 29 |
| mentality_positioning | 63 | 93 | 95 |
| mentality_vision | 64 | 95 | 76 |
| mentality_penalties | 65 | 75 | 88 |
| mentality_composure | 66 | 96 | 95 |
| defending_marking_awareness | 67 | 20 | 24 |
| defending_standing_tackle | 68 | 35 | 32 |
| defending_sliding_tackle | 69 | 24 | 24 |
| goalkeeping_diving | 70 | 6 | 7 |
| goalkeeping_handling | 71 | 11 | 11 |
| goalkeeping_kicking | 72 | 15 | 15 |
| goalkeeping_positioning | 73 | 14 | 14 |
| goalkeeping_reflexes | 74 | 8 | 11 |
| goalkeeping_speed | 75 | NA | NA |
| ls | 76 | 89+3 | 90+1 |
| st | 77 | 89+3 | 90+1 |
| rs | 78 | 89+3 | 90+1 |
| lw | 79 | 92 | 88 |
| lf | 80 | 93 | 89 |
| cf | 81 | 93 | 89 |
| rf | 82 | 93 | 89 |
| rw | 83 | 92 | 88 |
| lam | 84 | 93 | 86+3 |
| cam | 85 | 93 | 86+3 |
| ram | 86 | 93 | 86+3 |
| lm | 87 | 91+2 | 86+3 |
| lcm | 88 | 87+3 | 78+3 |
| cm | 89 | 87+3 | 78+3 |
| rcm | 90 | 87+3 | 78+3 |
| rm | 91 | 91+2 | 86+3 |
| lwb | 92 | 66+3 | 63+3 |
| ldm | 93 | 64+3 | 59+3 |
| cdm | 94 | 64+3 | 59+3 |
| rdm | 95 | 64+3 | 59+3 |
| rwb | 96 | 66+3 | 63+3 |
| lb | 97 | 61+3 | 60+3 |
| lcb | 98 | 50+3 | 53+3 |
| cb | 99 | 50+3 | 53+3 |
| rcb | 100 | 50+3 | 53+3 |
| rb | 101 | 61+3 | 60+3 |
| gk | 102 | 19+3 | 20+3 |
| player_face_url | 103 | https://cdn.sofifa.com/players/158/023/22_120.png | https://cdn.sofifa.com/players/020/801/22_120.png |
| club_logo_url | 104 | https://cdn.sofifa.com/teams/73/60.png | https://cdn.sofifa.com/teams/11/60.png |
| club_flag_url | 105 | https://cdn.sofifa.com/flags/fr.png | https://cdn.sofifa.com/flags/gb-eng.png |
| nation_logo_url | 106 | https://cdn.sofifa.com/teams/1369/60.png | https://cdn.sofifa.com/teams/1354/60.png |
| nation_flag_url | 107 | https://cdn.sofifa.com/flags/ar.png | https://cdn.sofifa.com/flags/pt.png |
Конечно, в дальнейшем отберём именно те признаки, которые предположительно помогут различать позицию игрока.
Так как мы занимаемся кластеризацией, у нас по результатам не будет возможности автоматически проверить результаты работы алгоритмов. Поэтому важно, чтобы результаты были осмысленными хотя бы для тех случаев, которые нам неплохо знакомы. Для этого возьмём две группы игроков, которые помогут убедиться в разумности построенных моделей:
Всегда возникает обоснованное опасение, что кластеризация произойдёт с учётом общего уровня игроков (например, для топовых игроков кластеризация получилась хорошо, а для “средних” игроков всё перемешалось), поэтому анализировать результаты на основе лишь топовых игроков не совсем корректно. Российские игроки в целом представлены равномерно по всей таблице, что позволяет оценивать результаты на основе наших знаний.
Найдём всех и посмотрим на табличку. Эти игроки нам пригодятся для того, чтобы увидеть, куда они попали после кластеризации. Ещё уберём тех игроков, для которых нет информации о зарплате (у них не фиксирован клуб и лига, это понадобится позже).
nashi_parni <- which(players_full$nationality == "Russia" & players_full$value_eur != 0)
url_nashi <- as.character(players_full$player_url[nashi_parni])
#далее то, что нужно для таблицы
cbind(players_full[nashi_parni, c(3,14,15,17,6,7,8,9)], "") %>% kbl() %>% kable_paper("hover", full_width = F, position = "left") %>%
column_spec(6, color = "black", background = spec_color(players_full[nashi_parni, 6])) %>%
column_spec(7, color = "black", background = spec_color(players_full[nashi_parni, 7])) %>%
column_spec(8, color = "black", background = spec_color(players_full[nashi_parni, 8])) %>%
column_spec(9, color = "black", background = spec_color(players_full[nashi_parni, 9])) %>%
column_spec(4, color = spec_color(as.integer(players_full[nashi_parni, 15]))) %>%
column_spec(2, bold = T, link = url_nashi)| short_name | club_name | league_name | club_position | overall | potential | value_eur | wage_eur | "" | |
|---|---|---|---|---|---|---|---|---|---|
| 221 | Mário Fernandes | PFC CSKA Moscow | Russian Premier League | RB | 82 | 82 | 26500000 | 57000 | |
| 391 | I. Akinfeev | PFC CSKA Moscow | Russian Premier League | GK | 80 | 80 | 2300000 | 26000 | |
| 617 | A. Golovin | AS Monaco | French Ligue 1 | LF | 79 | 83 | 24500000 | 53000 | |
| 759 | R. Zobnin | Spartak Moskva | Russian Premier League | RDM | 78 | 80 | 15000000 | 51000 | |
| 766 | A. Miranchuk | Atalanta | Italian Serie A | SUB | 78 | 80 | 17500000 | 46000 | |
| 807 | A. Lunev | Bayer 04 Leverkusen | German 1. Bundesliga | SUB | 78 | 79 | 11000000 | 39000 | |
| 882 | Guilherme | FC Lokomotiv Moscow | Russian Premier League | GK | 77 | 77 | 1200000 | 22000 | |
| 1034 | G. Dzhikiya | Spartak Moskva | Russian Premier League | LCB | 77 | 79 | 11000000 | 48000 | |
| 1180 | F. Smolov | FC Lokomotiv Moscow | Russian Premier League | RS | 76 | 76 | 6500000 | 47000 | |
| 1181 | A. Dzagoev | PFC CSKA Moscow | Russian Premier League | CAM | 76 | 76 | 6000000 | 40000 | |
| 1251 | D. Cheryshev | Valencia CF | Spain Primera Division | LM | 76 | 76 | 7000000 | 31000 | |
| 1325 | A. Miranchuk | FC Lokomotiv Moscow | Russian Premier League | SUB | 76 | 79 | 10000000 | 42000 | |
| 1561 | A. Kokorin | Fiorentina | Italian Serie A | SUB | 75 | 75 | 5500000 | 48000 | |
| 1806 | D. Barinov | FC Lokomotiv Moscow | Russian Premier League | RCM | 75 | 82 | 10500000 | 33000 | |
| 1890 | A. Sobolev | Spartak Moskva | Russian Premier League | ST | 75 | 80 | 8500000 | 45000 | |
| 2032 | G. Schennikov | PFC CSKA Moscow | Russian Premier League | SUB | 74 | 74 | 3600000 | 33000 | |
| 2283 | Z. Bakaev | Spartak Moskva | Russian Premier League | SUB | 74 | 78 | 6000000 | 41000 | |
| 2306 | R. Zhemaletdinov | FC Lokomotiv Moscow | Russian Premier League | RM | 74 | 78 | 6000000 | 33000 | |
| 2307 | A. Maksimenko | Spartak Moskva | Russian Premier League | GK | 74 | 81 | 7000000 | 26000 | |
| 2320 | F. Chalov | PFC CSKA Moscow | Russian Premier League | ST | 74 | 80 | 6500000 | 34000 | |
| 2932 | I. Oblyakov | PFC CSKA Moscow | Russian Premier League | LB | 73 | 80 | 6000000 | 29000 | |
| 3039 | I. Diveev | PFC CSKA Moscow | Russian Premier League | RCB | 73 | 82 | 6500000 | 21000 | |
| 3166 | V. Vasin | PFC CSKA Moscow | Russian Premier League | SUB | 72 | 72 | 1500000 | 26000 | |
| 3386 | A. Selikhov | Spartak Moskva | Russian Premier League | SUB | 72 | 74 | 2100000 | 26000 | |
| 3509 | I. Akhmetov | PFC CSKA Moscow | Russian Premier League | RDM | 72 | 78 | 3700000 | 25000 | |
| 3542 | D. Zhivoglyadov | FC Lokomotiv Moscow | Russian Premier League | SUB | 72 | 72 | 2300000 | 29000 | |
| 3544 | S. Iljutcenko | Jeonbuk Hyundai Motors | Korean K League 1 | ST | 72 | 72 | 2300000 | 10000 | |
| 3694 | K. Kuchaev | PFC CSKA Moscow | Russian Premier League | RM | 72 | 79 | 4700000 | 25000 | |
| 3877 | F. Kudryashov | Antalyaspor | Turkish Süper Lig | SUB | 71 | 71 | 700000 | 11000 | |
| 3898 | K. Rausch |
|
German 2. Bundesliga | SUB | 71 | 71 | 1400000 | 8000 | |
| 4078 | I. Kutepov | Spartak Moskva | Russian Premier League | SUB | 71 | 72 | 1900000 | 28000 | |
| 4215 | R. Mirzov | Spartak Moskva | Russian Premier League | SUB | 71 | 71 | 1900000 | 32000 | |
| 4521 | N. Rasskazov | Spartak Moskva | Russian Premier League | RB | 71 | 76 | 2600000 | 24000 | |
| 4554 | S. Magkeev | FC Lokomotiv Moscow | Russian Premier League | RCB | 71 | 80 | 4000000 | 22000 | |
| 4602 | A. Rebrov | Spartak Moskva | Russian Premier League | SUB | 70 | 70 | 180000 | 13000 | |
| 4621 | K. Nababkin | PFC CSKA Moscow | Russian Premier League | SUB | 70 | 70 | 525000 | 20000 | |
| 4624 | A. Eschenko | Spartak Moskva | Russian Premier League | SUB | 70 | 70 | 350000 | 18000 | |
| 4695 | E. Prib | Fortuna Düsseldorf | German 2. Bundesliga | LDM | 70 | 70 | 1300000 | 14000 | |
| 4714 | A. Zabolotnyi | PFC CSKA Moscow | Russian Premier League | SUB | 70 | 70 | 1600000 | 24000 | |
| 5338 | D. Kulikov | FC Lokomotiv Moscow | Russian Premier League | LCM | 70 | 79 | 3300000 | 19000 | |
| 5369 | D. Rybchinskiy | FC Lokomotiv Moscow | Russian Premier League | LM | 70 | 78 | 3600000 | 21000 | |
| 5370 | N. Umyarov | Spartak Moskva | Russian Premier League | SUB | 70 | 79 | 3400000 | 18000 | |
| 5945 | A. Zhirov | SV Sandhausen | German 2. Bundesliga | LCB | 69 | 69 | 1100000 | 4000 | |
| 6282 | K. Maradishvili | FC Lokomotiv Moscow | Russian Premier League | RES | 69 | 77 | 3000000 | 13000 | |
| 6283 | P. Maslov | Spartak Moskva | Russian Premier League | RES | 69 | 78 | 3000000 | 15000 | |
| 6403 | A. Silyanov | FC Lokomotiv Moscow | Russian Premier League | RB | 69 | 78 | 2900000 | 13000 | |
| 6539 | E. Bashkirov | Zagłębie Lubin | Polish T-Mobile Ekstraklasa | RDM | 68 | 68 | 1000000 | 4000 | |
| 6812 | A. Vasyutin | Djurgårdens IF | Swedish Allsvenskan | SUB | 68 | 73 | 1400000 | 3000 | |
| 6842 | N. Haikin | FK Bodø/Glimt | Norwegian Eliteserien | GK | 68 | 73 | 1400000 | 2000 | |
| 7073 | I. Zlobin | Futebol Clube de Famalicão | Portuguese Liga ZON SAGRES | SUB | 68 | 76 | 2300000 | 3000 | |
| 7437 | M. Mukhin | PFC CSKA Moscow | Russian Premier League | LDM | 68 | 79 | 2600000 | 9000 | |
| 8255 | M. Suleymanov | GZT Giresunspor | Turkish Süper Lig | SUB | 67 | 71 | 1500000 | 5000 | |
| 9347 | I. Zhigulev | Zagłębie Lubin | Polish T-Mobile Ekstraklasa | SUB | 66 | 71 | 1200000 | 3000 | |
| 9507 | N. Tiknizyan | FC Lokomotiv Moscow | Russian Premier League | RES | 66 | 77 | 1900000 | 11000 | |
| 9518 | A. Lomovitskiy | Spartak Moskva | Russian Premier League | SUB | 66 | 74 | 1900000 | 12000 | |
| 10292 | G. Melkadze | Spartak Moskva | Russian Premier League | SUB | 65 | 70 | 1100000 | 11000 | |
| 10540 | I. Shinozuka | Kashiwa Reysol | Japanese J. League Division 1 | RES | 65 | 66 | 850000 | 3000 | |
| 10694 | M. Ignatov | Spartak Moskva | Russian Premier League | SUB | 65 | 78 | 1800000 | 9000 | |
| 10746 | I. Gaponov | Spartak Moskva | Russian Premier League | RES | 65 | 74 | 1500000 | 10000 | |
| 11038 | M. Nenakhov | FC Lokomotiv Moscow | Russian Premier League | RES | 65 | 72 | 1400000 | 9000 | |
| 11306 | A. Mitryushkin | SG Dynamo Dresden | German 2. Bundesliga | SUB | 64 | 69 | 675000 | 3000 | |
| 11764 | L. Klassen | WSG Tirol | Austrian Football Bundesliga | LB | 64 | 71 | 1100000 | 2000 | |
| 11938 | V. Karpov | PFC CSKA Moscow | Russian Premier League | RES | 64 | 79 | 1300000 | 2000 | |
| 13233 | E. Shlyakov | AFC UTA Arad | Romanian Liga I | LB | 63 | 63 | 425000 | 2000 | |
| 13240 | E. Sevikyan | Levante Unión Deportiva | Spain Primera Division | RES | 63 | 77 | 1100000 | 3000 | |
| 14148 | N. Iosifov | Villarreal CF | Spain Primera Division | RES | 62 | 75 | 950000 | 4000 | |
| 14366 | S. Babkin | FC Lokomotiv Moscow | Russian Premier League | SUB | 62 | 77 | 925000 | 3000 | |
| 14393 | V. Yakovlev | PFC CSKA Moscow | Russian Premier League | RES | 62 | 75 | 950000 | 5000 | |
| 15876 | A. Savin | FC Lokomotiv Moscow | Russian Premier League | SUB | 60 | 70 | 475000 | 3000 | |
| 16442 | Y. Mikhailov | FC Schalke 04 | German 2. Bundesliga | SUB | 59 | 76 | 575000 | 750 | |
| 16794 | V. Molchan | Stade Malherbe Caen | French Ligue 2 | RES | 58 | 67 | 425000 | 750 | |
| 16890 | V. Cherny | DSC Arminia Bielefeld | German 1. Bundesliga | SUB | 58 | 76 | 525000 | 1000 | |
| 17476 | A. Chernov | Vejle Boldklub | Danish Superliga | SUB | 56 | 66 | 275000 | 1000 | |
| 17597 | D. Markitesov | Spartak Moskva | Russian Premier League | RES | 56 | 73 | 375000 | 6000 | |
| 17724 | A. Thomas | Seattle Sounders FC | USA Major League Soccer | RES | 56 | 63 | 250000 | 850 | |
| 17966 | D. Bokov | PFC CSKA Moscow | Russian Premier League | SUB | 55 | 74 | 275000 | 500 | |
| 18109 | D. Khudyakov | FC Lokomotiv Moscow | Russian Premier League | SUB | 55 | 75 | 300000 | 500 | |
| 18434 | T. Akmurzin | Spartak Moskva | Russian Premier League | RES | 53 | 63 | 180000 | 4000 | |
| 18487 | V. Torop | PFC CSKA Moscow | Russian Premier League | SUB | 53 | 75 | 275000 | 500 | |
| 18683 | A. Poplevchenkov | Spartak Moskva | Russian Premier League | RES | 52 | 66 | 170000 | 3000 | |
| 18853 | I. Repyakh | Vejle Boldklub | Danish Superliga | RES | 52 | 66 | 190000 | 1000 |
Русских здесь столько: 81.
Возьмём 50 самых лучших по оценке overall в FIFA футболистов (первые 50 строк).
top_world <- 1:50
url_world <- as.character(players_full$player_url[top_world])
cbind(1:50, players_full[top_world, c(3,14,15,17,6,7,8,9)]) %>% kbl() %>% kable_paper("hover", full_width = F, position = "left") %>%
column_spec(6, color = "black", background = spec_color(players_full[top_world, 6])) %>%
column_spec(7, color = "black", background = spec_color(players_full[top_world, 7])) %>%
column_spec(8, color = "black", background = spec_color(players_full[top_world, 8])) %>%
column_spec(9, color = "black", background = spec_color(players_full[top_world, 9])) %>%
column_spec(4, color = spec_color(as.integer(players_full[top_world, 15]))) %>%
column_spec(2, bold = T, link = url_world)| 1:50 | short_name | club_name | league_name | club_position | overall | potential | value_eur | wage_eur |
|---|---|---|---|---|---|---|---|---|
| 1 | L. Messi | Paris Saint-Germain | French Ligue 1 | RW | 93 | 93 | 78000000 | 320000 |
| 2 | R. Lewandowski | FC Bayern München | German 1. Bundesliga | ST | 92 | 92 | 119500000 | 270000 |
| 3 | Cristiano Ronaldo | Manchester United | English Premier League | ST | 91 | 91 | 45000000 | 270000 |
| 4 | Neymar Jr | Paris Saint-Germain | French Ligue 1 | LW | 91 | 91 | 129000000 | 270000 |
| 5 | K. De Bruyne | Manchester City | English Premier League | RCM | 91 | 91 | 125500000 | 350000 |
| 6 | J. Oblak | Atlético de Madrid | Spain Primera Division | GK | 91 | 93 | 112000000 | 130000 |
| 7 | K. Mbappé | Paris Saint-Germain | French Ligue 1 | ST | 91 | 95 | 194000000 | 230000 |
| 8 | M. Neuer | FC Bayern München | German 1. Bundesliga | GK | 90 | 90 | 13500000 | 86000 |
| 9 | M. ter Stegen | FC Barcelona | Spain Primera Division | GK | 90 | 92 | 99000000 | 250000 |
| 10 | H. Kane | Tottenham Hotspur | English Premier League | ST | 90 | 90 | 129500000 | 240000 |
| 11 | N. Kanté | Chelsea | English Premier League | RCM | 90 | 90 | 100000000 | 230000 |
| 12 | K. Benzema | Real Madrid CF | Spain Primera Division | CF | 89 | 89 | 66000000 | 350000 |
| 13 | T. Courtois | Real Madrid CF | Spain Primera Division | GK | 89 | 91 | 85500000 | 250000 |
| 14 | H. Son | Tottenham Hotspur | English Premier League | LW | 89 | 89 | 104000000 | 220000 |
| 15 | Casemiro | Real Madrid CF | Spain Primera Division | CDM | 89 | 89 | 88000000 | 310000 |
| 16 | V. van Dijk | Liverpool | English Premier League | LCB | 89 | 89 | 86000000 | 230000 |
| 17 | S. Mané | Liverpool | English Premier League | LW | 89 | 89 | 101000000 | 270000 |
| 18 | M. Salah | Liverpool | English Premier League | RW | 89 | 89 | 101000000 | 270000 |
| 19 | Ederson | Manchester City | English Premier League | GK | 89 | 91 | 94000000 | 200000 |
| 20 | J. Kimmich | FC Bayern München | German 1. Bundesliga | RDM | 89 | 90 | 108000000 | 160000 |
| 21 | Alisson | Liverpool | English Premier League | GK | 89 | 90 | 82000000 | 190000 |
| 22 | G. Donnarumma | Paris Saint-Germain | French Ligue 1 | GK | 89 | 93 | 119500000 | 110000 |
| 23 | Sergio Ramos | Paris Saint-Germain | French Ligue 1 | LCB | 88 | 88 | 24000000 | 115000 |
| 24 | L. Suárez | Atlético de Madrid | Spain Primera Division | RS | 88 | 88 | 44500000 | 135000 |
| 25 | T. Kroos | Real Madrid CF | Spain Primera Division | LCM | 88 | 88 | 75000000 | 310000 |
| 26 | R. Lukaku | Chelsea | English Premier League | ST | 88 | 88 | 93500000 | 260000 |
| 27 | K. Navas | Paris Saint-Germain | French Ligue 1 | SUB | 88 | 88 | 15500000 | 130000 |
| 28 | R. Sterling | Manchester City | English Premier League | SUB | 88 | 89 | 107500000 | 290000 |
| 29 | Bruno Fernandes | Manchester United | English Premier League | CAM | 88 | 89 | 107500000 | 250000 |
| 30 | E. Haaland | Borussia Dortmund | German 1. Bundesliga | RS | 88 | 93 | 137500000 | 110000 |
| 31 | S. Agüero | FC Barcelona | Spain Primera Division | ST | 87 | 87 | 51000000 | 260000 |
| 32 | H. Lloris | Tottenham Hotspur | English Premier League | GK | 87 | 87 | 13500000 | 125000 |
| 33 | L. Modrić | Real Madrid CF | Spain Primera Division | RCM | 87 | 87 | 32000000 | 190000 |
| 34 | A. Di María | Paris Saint-Germain | French Ligue 1 | SUB | 87 | 87 | 49500000 | 160000 |
| 35 | W. Szczęsny | Juventus | Italian Serie A | GK | 87 | 87 | 42000000 | 105000 |
| 36 | T. Müller | FC Bayern München | German 1. Bundesliga | CAM | 87 | 87 | 66000000 | 140000 |
| 37 | C. Immobile | Lazio | Italian Serie A | ST | 87 | 87 | 67500000 | 125000 |
| 38 | P. Pogba | Manchester United | English Premier League | RDM | 87 | 87 | 79500000 | 220000 |
| 39 | M. Verratti | Paris Saint-Germain | French Ligue 1 | LCM | 87 | 87 | 79500000 | 155000 |
| 40 | Marquinhos | Paris Saint-Germain | French Ligue 1 | RCB | 87 | 90 | 90500000 | 135000 |
| 41 | L. Goretzka | FC Bayern München | German 1. Bundesliga | LDM | 87 | 88 | 93000000 | 140000 |
| 42 | P. Dybala | Juventus | Italian Serie A | CAM | 87 | 88 | 93000000 | 160000 |
| 43 | A. Robertson | Liverpool | English Premier League | LB | 87 | 88 | 83500000 | 175000 |
| 44 | F. de Jong | FC Barcelona | Spain Primera Division | RCM | 87 | 92 | 119500000 | 210000 |
| 45 | T. Alexander-Arnold | Liverpool | English Premier League | RB | 87 | 92 | 114000000 | 150000 |
| 46 | J. Sancho | Manchester United | English Premier League | LM | 87 | 91 | 116500000 | 150000 |
| 47 | Rúben Dias | Manchester City | English Premier League | RCB | 87 | 91 | 102500000 | 170000 |
| 48 | G. Chiellini | Juventus | Italian Serie A | SUB | 86 | 86 | 12000000 | 88000 |
| 49 | S. Handanovič | Inter | Italian Serie A | GK | 86 | 86 | 7500000 | 78000 |
| 50 | M. Hummels | Borussia Dortmund | German 1. Bundesliga | LCB | 86 | 86 | 44000000 | 95000 |
Позиций в футболе достаточно много, особенно если рассматривать в классификации, которая дана здесь и в таблице.
## [1] RW ST LW RCM GK CF CDM LCB RDM RS LCM SUB CAM RCB LDM LB RB LM RM
## [20] LS CB RES RWB RF CM LWB LAM LF RAM
## 30 Levels: CAM CB CDM CF CM GK LAM LB LCB LCM LDM LF LM LS LW LWB RAM ... SUB
R и L — right и left, F и B — forward и back, C — center, S - striker
Если не учитывать голкиперов, то обычно мы говорим о защите, полузащите и нападении. В данном датасете присутствуют характеристики, которые потенциально могут помочь в определении предположительной позиции.
Давайте посмотрим, о каких характеристиках идёт речь:
## [1] "pace" "shooting"
## [3] "passing" "dribbling"
## [5] "defending" "physic"
## [7] "attacking_crossing" "attacking_finishing"
## [9] "attacking_heading_accuracy" "attacking_short_passing"
## [11] "attacking_volleys" "skill_dribbling"
## [13] "skill_curve" "skill_fk_accuracy"
## [15] "skill_long_passing" "skill_ball_control"
## [17] "movement_acceleration" "movement_sprint_speed"
## [19] "movement_agility" "movement_reactions"
## [21] "movement_balance" "power_shot_power"
## [23] "power_jumping" "power_stamina"
## [25] "power_strength" "power_long_shots"
## [27] "mentality_aggression" "mentality_interceptions"
## [29] "mentality_positioning" "mentality_vision"
## [31] "mentality_penalties" "mentality_composure"
## [33] "defending_marking_awareness" "defending_standing_tackle"
## [35] "defending_sliding_tackle" "goalkeeping_diving"
## [37] "goalkeeping_handling" "goalkeeping_kicking"
## [39] "goalkeeping_positioning" "goalkeeping_reflexes"
Кажется, что эти характеристики должны хорошо различать атакующих игроков от игроков защиты и полузащиты. Полузащиту в данном случае можно воспринимать как универсальных игроков. Здесь нет намёка на правый/левый фланг и правша/левша, поэтому надеемся, что этот фактор не будет влиять на формирование кластеров.
Для того, чтобы кластеризация не пошла по возрасту/потенциалу/общему уровню игры/стоимости, эти признаки мы тоже не включаем.
Так как некоторые характеристик для голкиперов отсутствуют, да и явно есть отличие между вратарями и полевыми игроками, мы изымем их из рассмотрения. Характеристики, которые начинаются с “goalkeeping” мы оставим, они могут помочь различать защитников, которые по долгу службы должны участвовать в защите ворот.
## [1] 2132
Не так их и много.
Ещё одна проблема заключается в том, что выборка большая и может включать в себя потенциальные неоднородности, которые хотелось бы избежать. Например, в низших лигах границы между игроками могут быть размыты сильнее. Посмотрим, сколько игроков останется, если оставим только игроков команд высших лиг.
## [1] 14857
Также, по этим признакам у нас не должно быть NA, уберём их позже, их немного.
Таким образом, остаётся столько футболистов:
## [1] 13193
Признаков много, поэтому проведём минимальный анализ. Сделаем два датафрейма, один с интересующими нас признаками, другой — с общей информацией об игроке, чтобы потом удобно было анализировать результат. NA уберём, как обещали.
players <- players_full[top_leagues & !goalkeepers, skills_vars]
players <- na.omit(players)
dim(players)## [1] 13193 40
players_info <- players_full[top_leagues & !goalkeepers, c(3,6,7,8,9,14,17,15,22,2)]
players_info <- na.omit(players_info)
dim(players_info)## [1] 13193 10
## pace shooting passing dribbling defending
## Min. :28.00 Min. :18.0 Min. :25.00 Min. :26.00 Min. :15.00
## 1st Qu.:62.00 1st Qu.:42.0 1st Qu.:51.00 1st Qu.:57.00 1st Qu.:38.00
## Median :69.00 Median :55.0 Median :58.00 Median :64.00 Median :56.00
## Mean :68.33 Mean :52.8 Mean :57.88 Mean :63.03 Mean :52.03
## 3rd Qu.:76.00 3rd Qu.:64.0 3rd Qu.:65.00 3rd Qu.:70.00 3rd Qu.:65.00
## Max. :97.00 Max. :94.0 Max. :93.00 Max. :95.00 Max. :91.00
## physic attacking_crossing attacking_finishing
## Min. :29.00 Min. :15.00 Min. :10.00
## 1st Qu.:59.00 1st Qu.:45.00 1st Qu.:37.00
## Median :66.00 Median :56.00 Median :53.00
## Mean :64.89 Mean :54.56 Mean :50.65
## 3rd Qu.:72.00 3rd Qu.:65.00 3rd Qu.:64.00
## Max. :90.00 Max. :94.00 Max. :95.00
## attacking_heading_accuracy attacking_short_passing attacking_volleys
## Min. :17.00 Min. :23.00 Min. :10.00
## 1st Qu.:48.00 1st Qu.:58.00 1st Qu.:35.00
## Median :57.00 Median :64.00 Median :47.00
## Mean :56.75 Mean :63.41 Mean :46.89
## 3rd Qu.:65.00 3rd Qu.:70.00 3rd Qu.:58.00
## Max. :93.00 Max. :94.00 Max. :90.00
## skill_dribbling skill_curve skill_fk_accuracy skill_long_passing
## Min. :18.0 Min. :12.00 Min. :10.00 Min. :20.00
## 1st Qu.:55.0 1st Qu.:40.00 1st Qu.:34.00 1st Qu.:50.00
## Median :63.0 Median :52.00 Median :44.00 Median :59.00
## Mean :61.4 Mean :51.93 Mean :46.27 Mean :57.09
## 3rd Qu.:70.0 3rd Qu.:64.00 3rd Qu.:58.00 3rd Qu.:66.00
## Max. :96.0 Max. :94.00 Max. :94.00 Max. :93.00
## skill_ball_control movement_acceleration movement_sprint_speed
## Min. :24.00 Min. :27.00 Min. :27.00
## 1st Qu.:58.00 1st Qu.:62.00 1st Qu.:63.00
## Median :65.00 Median :69.00 Median :69.00
## Mean :63.91 Mean :68.29 Mean :68.34
## 3rd Qu.:70.00 3rd Qu.:76.00 3rd Qu.:76.00
## Max. :96.00 Max. :97.00 Max. :97.00
## movement_agility movement_reactions movement_balance power_shot_power
## Min. :27.00 Min. :29.00 Min. :26.00 Min. :20.00
## 1st Qu.:59.00 1st Qu.:56.00 1st Qu.:60.00 1st Qu.:51.00
## Median :68.00 Median :62.00 Median :68.00 Median :61.00
## Mean :66.68 Mean :62.36 Mean :66.94 Mean :59.65
## 3rd Qu.:75.00 3rd Qu.:68.00 3rd Qu.:75.00 3rd Qu.:70.00
## Max. :96.00 Max. :94.00 Max. :96.00 Max. :95.00
## power_jumping power_stamina power_strength power_long_shots
## Min. :29.00 Min. :24.0 Min. :19.00 Min. :11.00
## 1st Qu.:58.00 1st Qu.:61.0 1st Qu.:58.00 1st Qu.:40.00
## Median :66.00 Median :68.0 Median :67.00 Median :54.00
## Mean :65.77 Mean :67.4 Mean :65.59 Mean :51.59
## 3rd Qu.:74.00 3rd Qu.:75.0 3rd Qu.:74.00 3rd Qu.:64.00
## Max. :95.00 Max. :97.0 Max. :96.00 Max. :94.00
## mentality_aggression mentality_interceptions mentality_positioning
## Min. :20.00 Min. :10.00 Min. :12.00
## 1st Qu.:50.00 1st Qu.:35.00 1st Qu.:48.00
## Median :61.00 Median :56.00 Median :58.00
## Mean :59.65 Mean :50.92 Mean :55.88
## 3rd Qu.:70.00 3rd Qu.:65.00 3rd Qu.:66.00
## Max. :95.00 Max. :91.00 Max. :96.00
## mentality_vision mentality_penalties mentality_composure
## Min. :13.00 Min. :13.00 Min. :30.00
## 1st Qu.:48.00 1st Qu.:42.00 1st Qu.:53.00
## Median :58.00 Median :51.00 Median :61.00
## Mean :56.35 Mean :51.85 Mean :60.57
## 3rd Qu.:66.00 3rd Qu.:61.00 3rd Qu.:68.00
## Max. :95.00 Max. :93.00 Max. :96.00
## defending_marking_awareness defending_standing_tackle defending_sliding_tackle
## Min. :10.00 Min. :10.00 Min. :10.00
## 1st Qu.:37.00 1st Qu.:37.00 1st Qu.:34.00
## Median :55.00 Median :59.00 Median :56.00
## Mean :51.04 Mean :52.63 Mean :50.22
## 3rd Qu.:65.00 3rd Qu.:67.00 3rd Qu.:65.00
## Max. :93.00 Max. :93.00 Max. :92.00
## goalkeeping_diving goalkeeping_handling goalkeeping_kicking
## Min. : 2.00 Min. : 2.00 Min. : 2.00
## 1st Qu.: 8.00 1st Qu.: 8.00 1st Qu.: 8.00
## Median :10.00 Median :10.00 Median :10.00
## Mean :10.34 Mean :10.37 Mean :10.38
## 3rd Qu.:13.00 3rd Qu.:13.00 3rd Qu.:13.00
## Max. :29.00 Max. :33.00 Max. :31.00
## goalkeeping_positioning goalkeeping_reflexes
## Min. : 2.00 Min. : 2.00
## 1st Qu.: 8.00 1st Qu.: 8.00
## Median :10.00 Median :10.00
## Mean :10.37 Mean :10.33
## 3rd Qu.:13.00 3rd Qu.:13.00
## Max. :33.00 Max. :37.00
## No id variables; using all as measure variables
p <- ggplot(data = players_m, aes(y=variable, x=value, fill = variable, alpha = 0.7)) +
geom_boxplot() + geom_violin() + scale_fill_manual(values = viridis(6)) + guides(fill = "none")
p ## No id variables; using all as measure variables
p <- ggplot(data = players_m, aes(y=variable, x=value, fill = variable, alpha = 0.7)) +
geom_boxplot() + geom_violin() + scale_fill_manual(values = viridis(6)) + guides(fill = "none")
p ## No id variables; using all as measure variables
p <- ggplot(data = players_m, aes(y=variable, x=value, fill = variable, alpha = 0.7)) +
geom_boxplot() + geom_violin() + scale_fill_manual(values = viridis(6)) + guides(fill = "none")
p ## No id variables; using all as measure variables
p <- ggplot(data = players_m, aes(y=variable, x=value, fill = variable, alpha = 0.7)) +
geom_boxplot() + geom_violin() + scale_fill_manual(values = viridis(6)) + guides(fill = "none")
p ## No id variables; using all as measure variables
p <- ggplot(data = players_m, aes(y=variable, x=value, fill = variable, alpha = 0.7)) +
geom_boxplot() + geom_violin() + scale_fill_manual(values = viridis(6)) + guides(fill = "none")
p ## No id variables; using all as measure variables
p <- ggplot(data = players_m, aes(y=variable, x=value, fill = variable, alpha = 0.7)) +
geom_boxplot() + geom_violin() + scale_fill_manual(values = viridis(6)) + guides(fill = "none")
p ## No id variables; using all as measure variables
p <- ggplot(data = players_m, aes(y=variable, x=value, fill = variable, alpha = 0.7)) +
geom_boxplot() + geom_violin() + scale_fill_manual(values = viridis(6)) + guides(fill = "none")
p ## No id variables; using all as measure variables
p <- ggplot(data = players_m, aes(y=variable, x=value, fill = variable, alpha = 0.7)) +
geom_boxplot() + geom_violin() + scale_fill_manual(values = viridis(6)) + guides(fill = "none")
p Как можно видеть, многие из приведённых графиков бимодальны, например, defending и attacking, что может быть хорошим знаком того, что кластеризация у нас получится (и может даже в нормальной модели).
Что касается вратарских качеств: их, по всей видимости можно (и нужно) убрать, они вряд ли нам помогут.
Попробуем сократить размерность пространства признаков и посмотрим на biplot.
res$var$coord %>% kbl() %>% kable_paper("hover", full_width = F, position = "left") %>%
column_spec(2, color = "black", background = spec_color(abs(res$var$coord[,1]))) %>%
column_spec(3, color = "black", background = spec_color(abs(res$var$coord[,2])))| Dim.1 | Dim.2 | Dim.3 | Dim.4 | Dim.5 | |
|---|---|---|---|---|---|
| pace | 0.4810878 | -0.3397358 | 0.5532932 | 0.5354779 | -0.1422583 |
| shooting | 0.9032270 | -0.1764423 | -0.3133115 | 0.0116386 | 0.0303722 |
| passing | 0.8830859 | 0.3290523 | 0.1259973 | -0.2263101 | -0.0535333 |
| dribbling | 0.9434600 | 0.0061936 | 0.1473443 | -0.0021739 | -0.0647130 |
| defending | -0.1760841 | 0.9241698 | 0.2736376 | -0.0832487 | 0.0197602 |
| physic | 0.0825704 | 0.7710660 | -0.3194985 | 0.4488759 | -0.0735207 |
| attacking_crossing | 0.7480857 | 0.1460340 | 0.3219942 | -0.1304374 | -0.0563095 |
| attacking_finishing | 0.8360515 | -0.2990904 | -0.3078603 | 0.0445384 | 0.0005752 |
| attacking_heading_accuracy | 0.0465929 | 0.5391675 | -0.5346967 | 0.3781368 | 0.0146001 |
| attacking_short_passing | 0.7372529 | 0.4887905 | 0.0363803 | -0.1623703 | -0.1103729 |
| attacking_volleys | 0.8232418 | -0.1692462 | -0.3154515 | 0.0055064 | 0.0835911 |
| skill_dribbling | 0.9113799 | -0.0511853 | 0.1082247 | -0.0205440 | -0.1151604 |
| skill_curve | 0.8575669 | 0.0372907 | 0.0368226 | -0.1682614 | 0.0823571 |
| skill_fk_accuracy | 0.7554941 | 0.0777018 | -0.0306113 | -0.3012300 | 0.1714579 |
| skill_long_passing | 0.6030437 | 0.5503079 | 0.1477062 | -0.3023431 | -0.0650349 |
| skill_ball_control | 0.8845116 | 0.2018498 | -0.0044121 | -0.0366795 | -0.1223450 |
| movement_acceleration | 0.5012476 | -0.3582338 | 0.5724073 | 0.4479059 | -0.0805458 |
| movement_sprint_speed | 0.4353543 | -0.3036164 | 0.5036966 | 0.5753058 | -0.1843606 |
| movement_agility | 0.6818991 | -0.2537280 | 0.4730854 | 0.1555667 | 0.1537129 |
| movement_reactions | 0.6279031 | 0.5477953 | -0.1233249 | 0.1399321 | -0.0346021 |
| movement_balance | 0.4875317 | -0.2721817 | 0.5589433 | -0.0640336 | 0.4047186 |
| power_shot_power | 0.8113908 | 0.0732114 | -0.3252816 | 0.0177948 | 0.0340333 |
| power_jumping | -0.0058226 | 0.3812037 | -0.1021994 | 0.5726762 | 0.5877798 |
| power_stamina | 0.3794141 | 0.4973206 | 0.1683711 | 0.3592733 | -0.0099487 |
| power_strength | -0.0756547 | 0.5876625 | -0.5316462 | 0.4081719 | -0.1989595 |
| power_long_shots | 0.8775222 | -0.0490441 | -0.2327196 | -0.0729005 | 0.0679915 |
| mentality_aggression | 0.0844900 | 0.8016122 | -0.0636422 | 0.1727646 | 0.0786633 |
| mentality_interceptions | -0.1413894 | 0.8944380 | 0.2921975 | -0.1043739 | 0.0347451 |
| mentality_positioning | 0.8733556 | -0.1558940 | -0.1148913 | 0.0450884 | -0.0319068 |
| mentality_vision | 0.8773886 | 0.1030932 | 0.0059282 | -0.2053569 | -0.0377516 |
| mentality_penalties | 0.7205974 | -0.1554450 | -0.3968978 | -0.0281083 | 0.1289800 |
| mentality_composure | 0.7108926 | 0.4457247 | -0.1586994 | 0.0260597 | -0.0219693 |
| defending_marking_awareness | -0.1593800 | 0.8850908 | 0.2872190 | -0.0947772 | 0.0177068 |
| defending_standing_tackle | -0.2017246 | 0.8749932 | 0.3306879 | -0.1258091 | 0.0090029 |
| defending_sliding_tackle | -0.2371442 | 0.8520072 | 0.3548579 | -0.1169937 | 0.0156048 |
Первый фактор, по всей видимости, характеризует атакующую игру, а второй — защиту. Первые две компоненты неплохо описывают дисперсию признаков, поэтому плоскость первых двух компонент весьма полезна для дальнейшей интерпретации.
Построим biplot и найдём некоторых игроков, чтобы интерпретировать полученный результат.
bestest <- c(1,4,3,5,29,15,23,16,61,47,56,115)
cbind(bestest, as.character(players_info$short_name[bestest]), as.character(players_info$club_position[bestest])) %>% kbl() %>% kable_paper("hover", full_width = F, position = "left")| bestest | ||
|---|---|---|
| 1 | L. Messi | RW |
| 4 | Neymar Jr | LW |
| 3 | Cristiano Ronaldo | ST |
| 5 | K. De Bruyne | RCM |
| 29 | M. Verratti | LCM |
| 15 | J. Kimmich | RDM |
| 23 | S. Agüero | ST |
| 16 | Sergio Ramos | LCB |
| 61 | E. Cavani | SUB |
| 47 | R. Mahrez | RW |
| 56 | Rodri | CDM |
| 115 | L. Sané | LM |
Нельзя сказать, что получилось однозначно (из-за полузащиты). С Месси (1), Неймаром (4) и Де Брюйне (5, атакующий полузащитник) всё логично, они в атаке.
Махрез (47) сейчас полузащитник-вингер, однако помимо подключений к атакам, от этих полузащитников требуется защита их игровых зон от проходов крайних защитников и опорных соперника. Киммих (15) тоже полузащитник. А вот Агуэро (23), вообще говоря, нападающий. Родри (56) - опорный полузащитник, поэтому немного странно, что он там, где есть (хотя, опять же, мы видим только двумерную картинку). Впрочем, общая логика всё же присутствует.
Кстати, нумерация идёт по общему рейтингу, хорошо видим, что слева внизу индексы большие.
Отчётливо видим, что есть облака точек; можно увидеть два крупных или три поменьше.
Переходим к применению алгоритмов кластеризации. Рассмотрим несколько известных алгоритмов:
Kohonen’s Self-Organized Maps
Mclust
Иерархическая кластеризация
\(k\) средних
DBscan
Начнём с самоорганизующихся карт Кохонена, которые широко используются для уменьшения размерности данных.
start_time <- Sys.time()
set.seed(56788)
data_matrix <- as.matrix(scale(players))
# Create the SOM Grid - you generally have to specify the size of the
# training grid prior to training the SOM. Hexagonal and Circular
# topologies are possible
som_grid <- somgrid(xdim = 20, ydim=20, topo="hexagonal")
# Finally, train the SOM, options for the number of iterations,
# the learning rates, and the neighbourhood are available
som_model <- som(data_matrix,
grid=som_grid,
rlen=3000,
alpha=c(0.05,0.01),
keep.data = TRUE)
end_time <- Sys.time()
end_time - start_time## Time difference of 9.561088 mins
Начинаем с выбора сетки: все игроки расположатся в одной из 20х20 ячеек. Выбор размера базируется на колиичестве индивидов в каждой ячейке: их должно быть не менее 10 (см. дальше, как это увидеть).
rlen отвечает за число итераций. Число итераций выбирается с использованием графика ниже.
На данной картинке показано, сколько индивидов находится в каждой из ячеек. Если смотреть на это как на карту, это аналог плотности населения. Красный цвет - в данном случае означает мало. Если в одной ячейке много индивидов - значит оказалось, что они похожи друг на друга. При необходимости следует увеличивать размерность сетки, чтобы они стали различимыми. У нас есть одна ячейка с 70 индивидами
Это хорошо, что все игроки распределились по ячейкам более менее равномерно. Может быть нужно увеличить карту, чтобы получилось поменьше индивидов на ячейку, но пока оставим так.
Переходим к главным объектам - самим картам.
#здесь просто текстовые лейблы делаем
labs_som = rep("", nrow(players))
labs_som[1:100] = abbreviate(players_info$short_name[1:100], 10)
labs_som[players_info$nationality == "Russia"] = as.character(players_info$short_name[players_info$nationality == "Russia"])
# это чтобы легче было раглядеть, можно экспортировать в рабочую папку pdf файл
# pdf()
# plot(som_model, type = "mapping", labels = labs_som, cex = 0.3)
# plot(som_model, type = "mapping", labels = labs_som, cex = 0.5, xlim = c(18,20), ylim = c(13,18))
# plot(som_model, type = "mapping", labels = labs_som, cex = 0.5, xlim = c(18,20), ylim = c(11,16))Для лучшего осознания того, что вообще на этих картах изображено, давайте покажем, в каких ячейках оказались футболисты топ уровня и наши соотечественники (они смешаны).
Здесь неплохо видно, что произошло. Все топовые игроки сместились в левую часть, однако хорошие защитники образовали кластер в правом верхнем углу.
Если продолжать аналогию с картами, то
С интерпретацией других мест на карте сложнее. Может станет лучше дальше, когда будем раскрашивать карту по признакам.
Видим, что некоторая логика у такой карты есть. На вкладках можно чуть получше рассмотреть имена.
plot(som_model, type = "mapping", labels = labs_som, cex = 0.5)
text(x = 1, y = 16, cex = 2, labels = "midfield", col = "green")
text(x = 1, y = 12, cex = 2, labels = "attack", col = "red")
text(x = 16, y = 14.5, cex = 2, labels = "defence", col = "blue")Раскрасим ячейки по признакам, а также разделим карту на части, максимально отдалённые друг от друга, с помощью жирной линии (для этого используется иерархическая кластеризация, см. комментарии).
coolBlueHotRed <- function(n, alpha = 1) {rainbow(n, end=4/6, alpha=alpha)[n:1]}
#k есть число желаемых групп для разделения
som.hc <- cutree(hclust(object.distances(som_model, "codes")), k=2)
#pdf("heatmapkoh")
par(mfrow = c(2,3))
plot(som_model, type = "property",
property = getCodes(som_model)[, 1], main = names(players)[1], palette.name = coolBlueHotRed)
add.cluster.boundaries(som_model, som.hc, lwd = 4)
plot(som_model, type = "property",
property = getCodes(som_model)[, 2], main = names(players)[2], palette.name = coolBlueHotRed)
add.cluster.boundaries(som_model, som.hc, lwd = 4)
plot(som_model, type = "property",
property = getCodes(som_model)[, 3], main = names(players)[3], palette.name = coolBlueHotRed)
add.cluster.boundaries(som_model, som.hc, lwd = 4)
plot(som_model, type = "property",
property = getCodes(som_model)[, 4], main = names(players)[4], palette.name = coolBlueHotRed)
add.cluster.boundaries(som_model, som.hc, lwd = 4)
plot(som_model, type = "property",
property = getCodes(som_model)[, 5], main = names(players)[5], palette.name = coolBlueHotRed)
add.cluster.boundaries(som_model, som.hc, lwd = 4)
plot(som_model, type = "property",
property = getCodes(som_model)[, 6], main = names(players)[6], palette.name = coolBlueHotRed)
add.cluster.boundaries(som_model, som.hc, lwd = 4)Можно долго смотреть и анализировать, что получилось.
Если посмотреть на раскраску по shooting, то неудивительно, что в левой части расположились практически все атакующие игроки. Поднимаясь слева наверх, видим улучшение по параметру передач, физической форме, темпу, и более высокие показатели по защите, при этом немного ухудшается shooting - чистая качественная полузащита. С защитой тоже всё понятно - лучшие - в правом верхнем углу.
И так далее…
mclustТак как метод встречался ранее, пройдёмся по нему быстро. Предполагается смесь нескольких нормальных распределений.
Будем использовать известную библиотеку mclust(), которая строит сразу множество вариантов моделей кластеризации.
Параметр G отвечает за число кластеров. В данном случае будут строиться всевозможные модели от 1 до 4 кластеров.
Здесь посмотрим на тот выбор, который делает функция mclust(). Этот выбор основывается на посчитанных характеристиках качества модели BIC и ICL. Так как нам известно, что значения BIC и ICL есть случайные числа, можно посмотреть какие ещё варианты кластеризации близки по этим значениям.
## ----------------------------------------------------
## Gaussian finite mixture model fitted by EM algorithm
## ----------------------------------------------------
##
## Mclust VVV (ellipsoidal, varying volume, shape, and orientation) model with 4
## components:
##
## log-likelihood n df BIC ICL
## -1276747 13193 2663 -2578758 -2580290
##
## Clustering table:
## 1 2 3 4
## 4433 3386 3557 1817
Метод выбрал разбиение на 9 кластеров, но это много для нас (убрал эту часть для скорости).
Наш выбор основан на совокупности факторов:
Значение байесовского информационного критерия модели (BIC) (больше — лучше). Заметим, что значение BIC есть случайная величина, а значит будет весьма осмысленным рассмотреть несколько моделей с похожим BIC и разным числом кластеров и параметров.
Число кластеров и число оцениваемых параметров Заметим здесь, что при сопоставимом значении BIC будем выбирать наиболее простую модель с наименьшим числом оцениваемых параметров, так как чем меньше параметров приходится оценивать, тем меньше будет дисперсия соответствующих оценок при фиксированном размере выборки. Мы хотели бы получить до четырёх кластеров.
Посмотрим на все BIC, построим график.
playersBIC <- mclustBIC(players, G = 1:4)
bic_table <- playersBIC[,]
colnames(bic_table) <- colnames(playersBIC)
rownames(bic_table) <- 1:nrow(bic_table)| EII | VII | EEI | VEI | EVI | VVI | EEE | EVE | VEE | VVE | EEV | VEV | EVV | VVV |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| -3712817 | -3712817 | -3676579 | -3676579 | -3676579 | -3676579 | -2637060 | -2637060 | -2637060 | -2637060 | -2637060 | -2637060 | -2637060 | -2637060 |
| -3576970 | -3571687 | -3529337 | -3528933 | -3521822 | -3521883 | -2635007 | -2609310 | -2628676 | -2609399 | -2593260 | -2593265 | -2591822 | -2591824 |
| -3462468 | -3459282 | -3446585 | -3443482 | -3430271 | -3430030 | -2629694 | -2604264 | -2623955 | -2594873 | -2585700 | -2584229 | -2585531 | -2581217 |
| -3425646 | -3422434 | -3396388 | -3394445 | -3369304 | -3367675 | -2621635 | -2593938 | -2616944 | -2589624 | -2585512 | -2584792 | -2583824 | -2582665 |
Выбрали модель VVV, 3 (VVV означает, что эллипсоиды распределений вообще никаким образом не совпадают (разный поворот, разный объём, и тд), это максимальное количество параметров)
## ----------------------------------------------------
## Gaussian finite mixture model fitted by EM algorithm
## ----------------------------------------------------
##
## Mclust VVV (ellipsoidal, varying volume, shape, and orientation) model with 3
## components:
##
## log-likelihood n df BIC ICL
## -1281186 13193 1997 -2581319 -2582393
##
## Clustering table:
## 1 2 3
## 4166 5299 3728
На основании наших предположений и графика с главными компонентами можем именовать классы. Построим биплот, чтобы оценить результат визуально.
classes_mclust <- as.factor(players_mclust$classification)
table(players_mclust$classification)/num_players##
## 1 2 3
## 0.3157735 0.4016524 0.2825741
levels(classes_mclust) <- c("Attack","Midfielder","Defence")
fviz_pca_biplot(res,
label = "all",
col.ind = classes_mclust,
legend.title = "Players")Для model based методов качество кластеров можно оценить с помощью меры uncertainty, которая вычисляется так: из единицы вычитается вероятность наиболее вероятного класса. Uncertainty должна быть малой для наибольшего числа индивидов. Это весьма неплохо показывает, насколько классы пересекаются.
Мера встроена в полученный объект от mclust. Посмотрим на различные квантили:
## 60% 70% 80% 90% 95% 97.5%
## 0.0002385194 0.0024448416 0.0175566112 0.1098279718 0.2646207899 0.3779114734
## 99% 99.5%
## 0.4545168199 0.4790241120
Видим, что у абсолютного большинства индивидов мера uncertainty мала (у \(95\%\) индивидов мера меньше 0.25, что существенно лучше наихудшего возможного исхода в нашем случае \(G=3\): \(0.666\)), что хорошо оценивает работу метода.
Чтобы можно было сравнивать все методы, посчитаем within SS/between SS
# Subtract each value from the grand mean and get the number of observations in each cluster.
data.cent <- scale(players, scale=FALSE)
nrows <- table(classes_mclust)
TSS <- sum(data.cent^2)
WSS <- sapply(split(players, classes_mclust), function(x) sum(scale(x, scale=FALSE)^2))
BSS <- TSS - sum(WSS)
gmeans <- sapply(split(players, classes_mclust), colMeans)
means <- colMeans(players)
BSS <- sum(colSums((gmeans - means)^2) * nrows)
BSS/TSS## [1] 0.391346
Посмотрим на отдельныx игроков в таблице:
| rate | name | position | mclust |
|---|---|---|---|
| 1 | L. Messi | RW | Midfielder |
| 3 | Cristiano Ronaldo | ST | Midfielder |
| 4 | Neymar Jr | LW | Midfielder |
| 5 | K. De Bruyne | RCM | Midfielder |
| 15 | Casemiro | CDM | Defence |
| 18 | M. Salah | RW | Midfielder |
| 20 | J. Kimmich | RDM | Defence |
| 23 | Sergio Ramos | LCB | Attack |
| 31 | S. Agüero | ST | Midfielder |
| 33 | L. Modrić | RCM | Defence |
| 39 | M. Verratti | LCM | Defence |
| 40 | Marquinhos | RCB | Attack |
| 47 | Rúben Dias | RCB | Attack |
| 48 | G. Chiellini | SUB | Attack |
| 53 | Sergio Busquets | CDM | Defence |
| 59 | R. Mahrez | RW | Midfielder |
| 68 | Rodri | CDM | Defence |
| 75 | E. Cavani | SUB | Midfielder |
| 97 | M. de Ligt | LCB | Attack |
| 99 | Jesús Navas | RB | Defence |
| 100 | Piqué | LCB | Attack |
| 133 | L. Sané | LM | Midfielder |
russian <- (players_info$nationality == "Russia")
cbind(rate = rownames(players[russian,]), name = as.character(players_info$short_name[russian]), position = as.character(players_info$club_position[russian]), mclust = as.character(classes_mclust)[russian]) %>% kbl() %>% kable_paper(full_width = F, "hover") %>%
column_spec(4, color = "white", background = spec_color(as.numeric(classes_mclust[russian]))) %>%
column_spec(2, color = "blue", bold = T, link = players_info$player_url[russian])| rate | name | position | mclust |
|---|---|---|---|
| 221 | Mário Fernandes | RB | Defence |
| 617 | A. Golovin | LF | Defence |
| 759 | R. Zobnin | RDM | Defence |
| 766 | A. Miranchuk | SUB | Midfielder |
| 1034 | G. Dzhikiya | LCB | Attack |
| 1180 | F. Smolov | RS | Midfielder |
| 1181 | A. Dzagoev | CAM | Defence |
| 1251 | D. Cheryshev | LM | Midfielder |
| 1325 | A. Miranchuk | SUB | Midfielder |
| 1561 | A. Kokorin | SUB | Midfielder |
| 1806 | D. Barinov | RCM | Defence |
| 1890 | A. Sobolev | ST | Midfielder |
| 2032 | G. Schennikov | SUB | Defence |
| 2283 | Z. Bakaev | SUB | Midfielder |
| 2306 | R. Zhemaletdinov | RM | Midfielder |
| 2320 | F. Chalov | ST | Midfielder |
| 2932 | I. Oblyakov | LB | Midfielder |
| 3039 | I. Diveev | RCB | Attack |
| 3166 | V. Vasin | SUB | Attack |
| 3509 | I. Akhmetov | RDM | Defence |
| 3542 | D. Zhivoglyadov | SUB | Defence |
| 3544 | S. Iljutcenko | ST | Midfielder |
| 3694 | K. Kuchaev | RM | Midfielder |
| 3877 | F. Kudryashov | SUB | Attack |
| 4078 | I. Kutepov | SUB | Attack |
| 4215 | R. Mirzov | SUB | Midfielder |
| 4521 | N. Rasskazov | RB | Attack |
| 4554 | S. Magkeev | RCB | Attack |
| 4621 | K. Nababkin | SUB | Attack |
| 4624 | A. Eschenko | SUB | Attack |
| 4714 | A. Zabolotnyi | SUB | Midfielder |
| 5338 | D. Kulikov | LCM | Defence |
| 5369 | D. Rybchinskiy | LM | Midfielder |
| 5370 | N. Umyarov | SUB | Defence |
| 6282 | K. Maradishvili | RES | Defence |
| 6283 | P. Maslov | RES | Attack |
| 6403 | A. Silyanov | RB | Attack |
| 6539 | E. Bashkirov | RDM | Defence |
| 7437 | M. Mukhin | LDM | Midfielder |
| 8255 | M. Suleymanov | SUB | Midfielder |
| 9347 | I. Zhigulev | SUB | Defence |
| 9507 | N. Tiknizyan | RES | Defence |
| 9518 | A. Lomovitskiy | SUB | Midfielder |
| 10292 | G. Melkadze | SUB | Midfielder |
| 10540 | I. Shinozuka | RES | Midfielder |
| 10694 | M. Ignatov | SUB | Midfielder |
| 10746 | I. Gaponov | RES | Attack |
| 11038 | M. Nenakhov | RES | Attack |
| 11764 | L. Klassen | LB | Defence |
| 11938 | V. Karpov | RES | Attack |
| 13233 | E. Shlyakov | LB | Attack |
| 13240 | E. Sevikyan | RES | Midfielder |
| 14148 | N. Iosifov | RES | Midfielder |
| 14366 | S. Babkin | SUB | Midfielder |
| 14393 | V. Yakovlev | RES | Midfielder |
| 16890 | V. Cherny | SUB | Midfielder |
| 17597 | D. Markitesov | RES | Attack |
| 18853 | I. Repyakh | RES | Midfielder |
Видим множество разумных совпадений.
Вычисленные значения и рисунок ниже являются обоснованиями того, что нам может быть полезнее в данной задаче: использовать классическую евклидову метрику или же использовать метрику, основанную на корреляциях.
График для каждого футболиста представляет из себя значение рейтинга для каждого из 35 признаков. Видим, что графики “похожи” между собой по форме для футболистов защиты. Аналогично для атакующих игроков. На форму общий уровень не влияет, что для нас важно.
len_p <- length(names(players))
plot(1:len_p, players[1,], "l", col = "red", xlab = "variable", ylab = "points")
lines(1:len_p, players[2,], col = "red")
lines(1:len_p, players[1033,], col = "red", lty = 2)
lines(1:len_p, players[11,], col = "blue")
lines(1:len_p, players[12,], col = "blue")
lines(1:len_p, players[3180,], col = "blue", lty = 2)
cor_dist <- as.matrix(as.dist(1 - cor(t(players[c(1,2, 1033, 11,12, 3180),])), diag = TRUE, upper = TRUE))
cor_dist## 1 2 1180 15 16 3877
## 1 0.0000000 0.1737571 0.1382049 1.5254286 1.5830255 1.4685641
## 2 0.1737571 0.0000000 0.0796193 1.2923222 1.4420318 1.4343929
## 1180 0.1382049 0.0796193 0.0000000 1.4307386 1.4845815 1.3283272
## 15 1.5254286 1.2923222 1.4307386 0.0000000 0.2588747 0.6481733
## 16 1.5830255 1.4420318 1.4845815 0.2588747 0.0000000 0.4143578
## 3877 1.4685641 1.4343929 1.3283272 0.6481733 0.4143578 0.0000000
eucl_dist <- as.matrix(dist(players[c(1,2, 1033, 11,12, 3180),], diag = TRUE, upper = TRUE))
eucl_dist/max(eucl_dist) #transform to new scale for comfort## 1 2 1180 15 16 3877
## 1 0.0000000 0.3842915 0.4878091 0.8847879 1.0000000 0.9969132
## 2 0.3842915 0.0000000 0.3817451 0.6918428 0.8253046 0.8645375
## 1180 0.4878091 0.3817451 0.0000000 0.7534426 0.8237432 0.6878652
## 15 0.8847879 0.6918428 0.7534426 0.0000000 0.3027049 0.5726006
## 16 1.0000000 0.8253046 0.8237432 0.3027049 0.0000000 0.4989325
## 3877 0.9969132 0.8645375 0.6878652 0.5726006 0.4989325 0.0000000
Например, для наших целей важно, чтобы игроки обороны (вне зависимости от их рейтинга!) были близки друг к другу, но при этом далеки от игроков защиты. Именно такую ситуацию мы и наблюдаем при выборе корреляции в качестве расстояния: сформировались соответствующие плеяды.
Можно посмотреть, насколько корреляционная метрика здесь подходит лучше, посмотрев на межкластерное расстояние.
В случае корреляционной метрики:
## [1] 0.1318756
В случае евклидовой:
## [1] 0.3490876
Сумма расстояний между индивидами из тех же кластеров делится на сумму расстояний между объектами разных кластеров. Чем меньше, тем лучше. Видим, что корреляционная метрика несколько лучше.
Здесь используем метрику корреляционную. Используем complete linkage, так как рассчитываем на сферичность кластеров (исходя из PCA).
players_dist <- as.dist(1 - cor(t(players)))
players_hclust <- hclust(players_dist, method="complete")
plot(players_hclust, labels = labs_som, cex = 0.4, main = "Dendrogram (Complete linkage)")classes_hclust <- cutree(players_hclust, k = 3)
classes_hclust <- as.factor(classes_hclust)
levels(classes_hclust) <- c("Attack", "Defence", "Midfielder")
table(classes_hclust)/num_players## classes_hclust
## Attack Defence Midfielder
## 0.4306071 0.5488517 0.0205412
Кластер Midfielder стал значительно меньше (по сравнению с mclust). Поэтому учитывая размер класса, можно сказать, что логично выкинуть Midfielder и мы получим простую классификацию на атакующего и защиту.
classes_hclust <- cutree(players_hclust, k = 2)
classes_hclust <- as.factor(classes_hclust)
levels(classes_hclust) <- c("Attack", "Defence")
table(classes_hclust)/num_players## classes_hclust
## Attack Defence
## 0.4511483 0.5488517
Посмотрим на биплот, убедимся в том, что результат в целом похож на то, что мы видели ранее.
Тоже вполне полезно и неплохо интерпретируемо.
Если же сделать то же самое с обычной евклидовой метрикой, получим такие классы:
players_dist1 <- dist(players)
players_hclust1 <- hclust(players_dist1, method="complete")
classes_hclust1 <- cutree(players_hclust1, k = 3)
classes_hclust1 <- as.factor(classes_hclust1)
levels(classes_hclust1) <- c("Attack", "Good defence", "Not the best players")
table(classes_hclust1)/num_players## classes_hclust1
## Attack Good defence Not the best players
## 0.4334875 0.3517775 0.2147351
Здесь получаем как раз ту проблему, о которой говорили: всё распределирось равномерно, однако слабые игроки ушли в третий класс. Если построить для двух кластеров, получим картинку, похожую на случай с корреляционной метрикой. Так что видим, что использование корреляционной метрики здесь даёт более устойчивый результат.
Здесь опять же надо сделать замечание, что кластеры мы делали с помощью другого функционала (минимизировали корреляцию между индивидами), поэтому то, что приведено дальше — не совсем верно (но для сравнения с другими методами допустимо).
# Subtract each value from the grand mean and get the number of observations in each cluster.
data.cent <- scale(players, scale=FALSE)
nrows <- table(cutree(players_hclust, k = 2))
TSS <- sum(data.cent^2)
WSS <- sapply(split(players, classes_hclust), function(x) sum(scale(x, scale=FALSE)^2))
BSS <- TSS - sum(WSS)
gmeans <- sapply(split(players, classes_hclust), colMeans)
means <- colMeans(players)
BSS <- sum(colSums((gmeans - means)^2) * nrows)
BSS/TSS## [1] 0.2723084
Здесь функционал похуже, чем ранее с mclust.
set.seed(28)
num_of_clust <- 3
players_kmeans <- kmeans(players, num_of_clust)
classes_kmeans <- players_kmeans$cluster
classes_kmeans <- as.factor(classes_kmeans)
levels(classes_kmeans) <- c("Attack", "Good defence and midfield", "Not the best players")
table(classes_kmeans)/num_players## classes_kmeans
## Attack Good defence and midfield Not the best players
## 0.3438187 0.3266884 0.3294929
Результат как в случае иерархической кластеризации.
Всё же сравним с результатами mclust:
## classes_kmeans
## classes_mclust Attack Good defence and midfield Not the best players
## Attack 7 616 3543
## Midfielder 4459 719 121
## Defence 70 2975 683
Атака отделилась хорошо, видим устойчивость оценок. С остальными характеристиками не всё так хорошо.
Число BSS/TSS:
## [1] 0.4542364
DBscan здесь сложно работать, ибо кластеры смешаны.
Для DBscan нужен оптимальный радиус окрестности — это когда абсолютное большинство точек имеют рядом \(k\) соседей. Его можно определить приближённо с помощью kNN графика. Датасет у нас большой, но для начала попробуем взять 3 соседей (это станет min pts).
Возьмём \(\varepsilon = 55\). minPts это число точек в окрестности. Выбираем стандарт, чтобы сразу много не захватить.
Построим
classes_dbscan <- players_dbscan$cluster
fviz_pca_biplot(res,
label = "all",
col.ind = factor(classes_dbscan),
legend.title = "Players")Получилось плохо, а число кластеров мы регулировать не можем.
Попытаем счастье, взяв метрику с корреляциями.
players_dbscan <- dbscan(players_dist, eps = 0.2, minPts = 5)
classes_dbscan <- players_dbscan$clusterclasses_dbscan <- players_dbscan$cluster
fviz_pca_biplot(res,
label = "all",
col.ind = factor(classes_dbscan),
legend.title = "Players")Всё одинаково плохо и это сложно интерпретировать.
Если смотреть на таблицы и анализировать их, то mclust сработал очень неплохо, hclust с двумя классами и корреляционной метрикой тоже кластеризует достаточно хорошо.
cbind(rate = rownames(players[players_top,]), name = as.character(players_info$short_name[players_top]), position = as.character(players_info$club_position[players_top]), mclust = as.character(classes_mclust)[players_top],hclust = as.character(classes_hclust)[players_top], kmeans = as.character(classes_kmeans)[players_top], DBscan = as.character(classes_dbscan)[players_top] ) %>% kbl() %>% kable_paper(full_width = F, "hover") %>%
column_spec(2, color = "blue", bold = T, link = players_info$player_url[players_top])| rate | name | position | mclust | hclust | kmeans | DBscan |
|---|---|---|---|---|---|---|
| 1 | L. Messi | RW | Midfielder | Attack | Attack | 1 |
| 3 | Cristiano Ronaldo | ST | Midfielder | Attack | Attack | 1 |
| 4 | Neymar Jr | LW | Midfielder | Attack | Attack | 1 |
| 5 | K. De Bruyne | RCM | Midfielder | Attack | Good defence and midfield | 1 |
| 15 | Casemiro | CDM | Defence | Defence | Good defence and midfield | 1 |
| 18 | M. Salah | RW | Midfielder | Attack | Good defence and midfield | 1 |
| 20 | J. Kimmich | RDM | Defence | Defence | Good defence and midfield | 0 |
| 23 | Sergio Ramos | LCB | Attack | Defence | Good defence and midfield | 1 |
| 31 | S. Agüero | ST | Midfielder | Attack | Attack | 1 |
| 33 | L. Modrić | RCM | Defence | Attack | Good defence and midfield | 1 |
| 39 | M. Verratti | LCM | Defence | Attack | Good defence and midfield | 1 |
| 40 | Marquinhos | RCB | Attack | Defence | Good defence and midfield | 1 |
| 47 | Rúben Dias | RCB | Attack | Defence | Good defence and midfield | 1 |
| 48 | G. Chiellini | SUB | Attack | Defence | Good defence and midfield | 1 |
| 53 | Sergio Busquets | CDM | Defence | Defence | Good defence and midfield | 1 |
| 59 | R. Mahrez | RW | Midfielder | Attack | Attack | 1 |
| 68 | Rodri | CDM | Defence | Defence | Good defence and midfield | 1 |
| 75 | E. Cavani | SUB | Midfielder | Attack | Good defence and midfield | 1 |
| 97 | M. de Ligt | LCB | Attack | Defence | Good defence and midfield | 1 |
| 99 | Jesús Navas | RB | Defence | Attack | Good defence and midfield | 1 |
| 100 | Piqué | LCB | Attack | Defence | Good defence and midfield | 1 |
| 133 | L. Sané | LM | Midfielder | Attack | Attack | 1 |
cbind(rate = rownames(players[russian,]), name = as.character(players_info$short_name[russian]), position = as.character(players_info$club_position[russian]), mclust = as.character(classes_mclust)[russian],hclust = as.character(classes_hclust)[russian], kmeans = as.character(classes_kmeans)[russian], DBscan = as.character(classes_dbscan)[russian] ) %>% kbl() %>% kable_paper(full_width = F, "hover") %>%
column_spec(2, color = "blue", bold = T, link = players_info$player_url[russian])| rate | name | position | mclust | hclust | kmeans | DBscan |
|---|---|---|---|---|---|---|
| 221 | Mário Fernandes | RB | Defence | Defence | Good defence and midfield | 1 |
| 617 | A. Golovin | LF | Defence | Attack | Good defence and midfield | 1 |
| 759 | R. Zobnin | RDM | Defence | Defence | Good defence and midfield | 1 |
| 766 | A. Miranchuk | SUB | Midfielder | Attack | Attack | 1 |
| 1034 | G. Dzhikiya | LCB | Attack | Defence | Good defence and midfield | 1 |
| 1180 | F. Smolov | RS | Midfielder | Attack | Attack | 1 |
| 1181 | A. Dzagoev | CAM | Defence | Attack | Good defence and midfield | 0 |
| 1251 | D. Cheryshev | LM | Midfielder | Attack | Good defence and midfield | 1 |
| 1325 | A. Miranchuk | SUB | Midfielder | Attack | Attack | 1 |
| 1561 | A. Kokorin | SUB | Midfielder | Attack | Attack | 1 |
| 1806 | D. Barinov | RCM | Defence | Defence | Good defence and midfield | 1 |
| 1890 | A. Sobolev | ST | Midfielder | Attack | Attack | 1 |
| 2032 | G. Schennikov | SUB | Defence | Defence | Good defence and midfield | 1 |
| 2283 | Z. Bakaev | SUB | Midfielder | Attack | Attack | 1 |
| 2306 | R. Zhemaletdinov | RM | Midfielder | Attack | Attack | 1 |
| 2320 | F. Chalov | ST | Midfielder | Attack | Attack | 1 |
| 2932 | I. Oblyakov | LB | Midfielder | Attack | Good defence and midfield | 1 |
| 3039 | I. Diveev | RCB | Attack | Defence | Not the best players | 1 |
| 3166 | V. Vasin | SUB | Attack | Defence | Not the best players | 1 |
| 3509 | I. Akhmetov | RDM | Defence | Attack | Good defence and midfield | 1 |
| 3542 | D. Zhivoglyadov | SUB | Defence | Defence | Good defence and midfield | 1 |
| 3544 | S. Iljutcenko | ST | Midfielder | Attack | Attack | 1 |
| 3694 | K. Kuchaev | RM | Midfielder | Defence | Good defence and midfield | 1 |
| 3877 | F. Kudryashov | SUB | Attack | Defence | Good defence and midfield | 1 |
| 4078 | I. Kutepov | SUB | Attack | Defence | Not the best players | 1 |
| 4215 | R. Mirzov | SUB | Midfielder | Attack | Good defence and midfield | 1 |
| 4521 | N. Rasskazov | RB | Attack | Defence | Good defence and midfield | 1 |
| 4554 | S. Magkeev | RCB | Attack | Defence | Not the best players | 1 |
| 4621 | K. Nababkin | SUB | Attack | Defence | Good defence and midfield | 1 |
| 4624 | A. Eschenko | SUB | Attack | Defence | Good defence and midfield | 0 |
| 4714 | A. Zabolotnyi | SUB | Midfielder | Attack | Attack | 1 |
| 5338 | D. Kulikov | LCM | Defence | Defence | Good defence and midfield | 1 |
| 5369 | D. Rybchinskiy | LM | Midfielder | Defence | Good defence and midfield | 1 |
| 5370 | N. Umyarov | SUB | Defence | Defence | Good defence and midfield | 1 |
| 6282 | K. Maradishvili | RES | Defence | Defence | Good defence and midfield | 1 |
| 6283 | P. Maslov | RES | Attack | Defence | Not the best players | 1 |
| 6403 | A. Silyanov | RB | Attack | Defence | Not the best players | 1 |
| 6539 | E. Bashkirov | RDM | Defence | Attack | Good defence and midfield | 1 |
| 7437 | M. Mukhin | LDM | Midfielder | Defence | Good defence and midfield | 1 |
| 8255 | M. Suleymanov | SUB | Midfielder | Attack | Attack | 1 |
| 9347 | I. Zhigulev | SUB | Defence | Defence | Good defence and midfield | 1 |
| 9507 | N. Tiknizyan | RES | Defence | Defence | Good defence and midfield | 1 |
| 9518 | A. Lomovitskiy | SUB | Midfielder | Attack | Attack | 1 |
| 10292 | G. Melkadze | SUB | Midfielder | Attack | Attack | 1 |
| 10540 | I. Shinozuka | RES | Midfielder | Attack | Attack | 1 |
| 10694 | M. Ignatov | SUB | Midfielder | Attack | Attack | 1 |
| 10746 | I. Gaponov | RES | Attack | Defence | Not the best players | 1 |
| 11038 | M. Nenakhov | RES | Attack | Defence | Not the best players | 1 |
| 11764 | L. Klassen | LB | Defence | Defence | Not the best players | 1 |
| 11938 | V. Karpov | RES | Attack | Defence | Not the best players | 1 |
| 13233 | E. Shlyakov | LB | Attack | Defence | Not the best players | 1 |
| 13240 | E. Sevikyan | RES | Midfielder | Attack | Attack | 1 |
| 14148 | N. Iosifov | RES | Midfielder | Attack | Attack | 1 |
| 14366 | S. Babkin | SUB | Midfielder | Defence | Good defence and midfield | 1 |
| 14393 | V. Yakovlev | RES | Midfielder | Attack | Attack | 1 |
| 16890 | V. Cherny | SUB | Midfielder | Attack | Attack | 1 |
| 17597 | D. Markitesov | RES | Attack | Defence | Not the best players | 0 |
| 18853 | I. Repyakh | RES | Midfielder | Attack | Not the best players | 1 |
В данной ситуации, когда кластеры перемешаны, явно выигравает model based подход, с помощью mclust. Остальные методы сильно зависят от выбора метрики. SOM помогает получать некоторые интерпретации, однако со сходимостью могут быть проблемы.